package com.zzh.bc.db;

import com.zzh.bc.annotations.HColumn;
import com.zzh.bc.log.log;
import com.zzh.bc.model.KeyTypeModel;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class RSUtils {

    public static final ConcurrentHashMap<String, List<KeyTypeModel>> clzKey = new ConcurrentHashMap<>();

    /**
     * 将数据库查询到的数据封装成Bean
     *
     * @param rs  数据源
     * @param clz 实例
     * @param <T> 要封装成的类型
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> List<T> cast2ListBean(ResultSet rs, Class<?> clz) throws IllegalAccessException, InstantiationException, SQLException {
        if (rs == null) {
            return null;
        }
        List<T> dataList = new ArrayList<>();
        while (rs.next()) {
            T instance = getClassInstance(rs, clz);
            dataList.add(instance);
        }
        C3p0Utils.releaseRS(rs);
        C3p0Utils.releaseConnection();
        return dataList;
    }

    /**
     * 获取 T 类型的实例
     *
     * @param rs  数据库原始数据集合
     * @param clz 实体类型
     * @param <T> 实体类型泛型
     * @return T 类型实例化对象
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SQLException
     */
    private static <T> T getClassInstance(ResultSet rs, Class<?> clz) throws InstantiationException, IllegalAccessException, SQLException {
        T instance = (T) clz.newInstance();
        Field[] fields = instance.getClass().getDeclaredFields();
        List<KeyTypeModel> keyTypeModelList = clzKey.getOrDefault(clz.getCanonicalName(), new ArrayList<>());
        if (!keyTypeModelList.isEmpty()) {
            castCacheMap(fields, keyTypeModelList, instance, rs);
        } else {
            List<KeyTypeModel> list = new ArrayList<>();
            for (Field field : fields) {
                if (field.isAnnotationPresent(HColumn.class)) {
                    KeyTypeModel ktm = null;
                    HColumn col = field.getAnnotation(HColumn.class);
                    String columnName = col.columnName();
                    field.setAccessible(true);

                    field.set(instance, rs.getObject(columnName));
                    ktm = new KeyTypeModel(columnName, field.getName());
                    list.add(ktm);
                }
            }
            clzKey.put(clz.getCanonicalName(), list);
        }
        return instance;
    }

    /**
     * 获取注解和字段的映射关系。
     *
     * @param clz 类型
     * @return 集合
     */
    public static List<KeyTypeModel> getKeyTypeMapper(Class<?> clz) {
        log.d("RSUtils.getKeyTypeMapper: 获取注解和字段的映射关系 入口");
        List<KeyTypeModel> listCache = new ArrayList<>();
        List<KeyTypeModel> keyTypeModelList = clzKey.getOrDefault(clz.getCanonicalName(), listCache);
        if (keyTypeModelList.isEmpty()) {
            log.d("获取注解和字段的映射关系的缓存数据为空，进行字段映射");
            Object instance = null;
            try {
                instance = clz.getDeclaredConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
            Field[] fields = instance.getClass().getDeclaredFields();
            log.d("字段映射大小：" + fields.length);
            for (Field field : fields) {
                log.d("字段名称：" + field.getName());
                if (field.isAnnotationPresent(HColumn.class)) {
                    HColumn col = field.getAnnotation(HColumn.class);
                    field.setAccessible(true);
                    String columnName = col.columnName();
                    log.d("字段列名映射关系：" + columnName + "， " + field.getName());
                    KeyTypeModel ktm = new KeyTypeModel(columnName, field.getName());
                    listCache.add(ktm);
                }
            }
            clzKey.put(clz.getCanonicalName(), listCache);
        } else {
            log.d("获取注解和字段的映射关系的缓存数据，映射数据量：" + keyTypeModelList.size());
            return keyTypeModelList;
        }
        return listCache;
    }

    /**
     * 将数据库查询到的数据封装成Bean
     *
     * @param rs  数据源
     * @param clz 实例
     * @param <T> 要封装成的类型
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T cast2Bean(ResultSet rs, Class<?> clz) throws IllegalAccessException, InstantiationException, SQLException {
        if (!rs.next()) {
            return null;
        }
        return getClassInstance(rs, clz);
    }

    private static <T> void castCacheMap(Field[] fields, List<KeyTypeModel> keyTypeModelList, T instance, ResultSet rs) throws IllegalAccessException, SQLException {
        for (Field field : fields) {
            field.setAccessible(true);
            for (KeyTypeModel model : keyTypeModelList) {
                if (field.getName().equals(model.getFieldName())) {
                    field.set(instance, rs.getObject(model.getKey()));
                    continue;
                }
            }
        }
    }
}
